【UNIX环境高级编程】 您所在的位置:网站首页 unix高级编程 pdf下载 【UNIX环境高级编程】

【UNIX环境高级编程】

2022-12-16 00:23| 来源: 网络整理| 查看: 265

【UNIX环境高级编程】

环境搭建

1、下载源码

wget http://apuebook.com/src.3e.tar.gz

2、解压

tar -zxvf src.3e.tar.gz

3、安装libbsd-dev

sudo apt-get install libbsd-dev

4、进入目录apue.3并编译

cd apue.3e make

5、复制文件

sudo cp ./include/apue.h /usr/include/ sudo cp ./lib/libapue.a /usr/local/lib/ sudo cp ./lib/libapue.a /usr/lib/ 本书涉及函数

文件I/O: dup() dup2() fcntl() 进程控制 fork() vfork() wait() waitpid() execl() execle() setuid() setgid()

第八章 进程控制 进程ID

0 :调度进程,是系统进程; 1 : init进程,是用户进程,由内核调用,不是内核中的系统进程; 2 : 守护进程,(负责支持虚拟存储器系统的分页操作); 僵尸进程:简单地说就是死了没人收尸的进程(eg.父进程处于休眠状态),父进程在子进程结束后没有释放子进程的资源,也没有获取子进程的相关信息; 孤儿进程:简单地说就是子进程没爸爸了(eg.父进程已经终止),此时该子进程的父进程变为init进程(init进程ID为1)。

fork() 和 vfork() :

fork() : 产生子进程之后,不能保证子进程和父进程谁先执行,谁先执行取决于内核的调度算法。 fork 创建子进程, 调用一次,返回两次,子进程返回0,父进程返回子进程ID。

vfork() : 产生的子进程先运行,在子进程调用exec或者exit之后父进程才可能被调度。在子进程调用exec或者exit之前,内核会让父进程处于休眠状态。

进程间的同步方法: 8.9节和10.16节(用信号)讲了。

例: fork C:

#include #include int main() { pid_t pid = fork(); if(pid printf("child: pid = %d, getpid = %d\n", pid, getpid()); } else { printf("parent: pid = %d, getpid = %d\n", pid, getpid()); } return 0; }

运行结果:

[^_^ ~ 13:02 55]$./test parent: pid = 17838, getpid = 17837 child: pid = 0, getpid = 17838

vfork:

#include "apue.h" int globvar = 6; /* external variable in initialized data */ int main(void) { int var; /* automatic variable on the stack */ pid_t pid; var = 88; printf("before vfork\n"); /* we don't flush stdio */ if ((pid = vfork()) /* child */ globvar++; /* modify parent's variables */ var++; exit(0); //这一句注释掉,为什么printf会打印两次 /* child terminates */ } /* parent continues here */ printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var); exit(0); }

运行结果: 1、不注释exit(0);

virtual-machine:~/codewithbook/apue.3e/proc$ ./vfork1 before vfork pid = 38302, glob = 7, var = 89

2、注释掉exit(0);

virtual-machine:~/codewithbook/apue.3e/proc$ ./vfork1 before vfork pid = 38141, glob = 7, var = 89 pid = 38140, glob = 7, var = 89

exit(0); //这一句注释掉,为什么printf会打印两次,没懂

进程终止方式

正常终止(5种):return,exit,_exit或_Exit,进程最后一个线程执行return,进程最后一个线程调用pthread_exit 异常终止(3种):abort(会产生SIGABRT信号),进程接收到某个信号而终止(eg.某数除以0,进程会收到内核传来的信号),最后一个线程对”取消“请求作出响应。

进程退出状态码 信号编号信号名称退出码信号描述默认处理方式6SIGARBT134异常退出异常退出、core dump wait() 和 waitpid()

wait() : 获取子进程的终止状态,当子进程未终止,其父进程调用了wait之后就会死等(即父进程此时会处于阻塞状态),直到子进程终止,此时读取子进程的终止状态。 waitpid() : 可以获取指定pid进程的终止状态;不会像wait()一样处于阻塞状态

父子进程资源竞争

父子进程谁先执行不能确定,谁先执行取决于内核的调度算法。 eg. C:

#include "apue.h" static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); if ((pid = fork()) charatatime("output from child\n"); } else { charatatime("output from parent\n"); } exit(0); } static void charatatime(char *str) { char *ptr; int c; setbuf(stdout, NULL); /* set unbuffered */ for (ptr = str; (c = *ptr++) != 0; ) putc(c, stdout); }

运行结果:

virtual-machine:~/codewithbook/apue.3e/proc$ ./tellwait1 output from parent output from child

备注:这两行的打印顺序不固定,取决于内核的调度算法。 要想控制子进程先执行,可以采用下面的方式。 C:

static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); if ((pid = fork()) //子进程 charatatime("output from child\n"); TELL_PARENT(getppid()); //通知父进程 } else { //父进程 WAIT_CHILD(); //等待子进程执行 charatatime("output from parent\n"); } exit(0); } static void charatatime(char *str) { char *ptr; int c; setbuf(stdout, NULL); /* set unbuffered */ for (ptr = str; (c = *ptr++) != 0; ) putc(c, stdout); }

执行结果:

virtual-machine:~/codewithbook/apue.3e/proc$ ./tellwait1 output from child output from parent exec 和 execle

调用exec并不创建新的进程,所以调用前后进程ID不变。调用exec只是用磁盘上一个新程序替换当前进程的堆、栈、数据段、代码段。

execl : eg1. C:

#include #include #include int main(void) { printf("entering main process---\n"); execl("/bin/ls","ls",NULL); printf("exiting main process ----\n"); return 0; }

运行结果:

[^_^ ~/gdb 18:59 91]$./execl entering main process--- Chapter_01 Chapter_03 Chapter_05 Chapter_07 execl gdb-12.1 README.html Chapter_02 Chapter_04 Chapter_06 Chapter_08 execl.c gdb-12.1.tar.gz [^_^ ~/gdb 18:59 92]$ [^_^ ~/gdb 18:59 92]$ls Chapter_01 Chapter_03 Chapter_05 Chapter_07 execl gdb-12.1 README.html Chapter_02 Chapter_04 Chapter_06 Chapter_08 execl.c gdb-12.1.tar.gz

结果分析: 执行execl(“/bin/ls”,“ls”,NULL); 相当于执行/bin/ls,而不再执行之前的main。(调用exec只是用磁盘上一个新程序替换当前进程的堆、栈、数据段、代码段。)

eg2. C: execl.c

#include #include #include int main(int argc, char *argv[]) { printf("Entering main ...\n"); int ret; ret =execl("./test","test", NULL); if(ret == -1) perror("execl error"); printf("Exiting main ...\n"); return 0; }

test.c

#include #include extern char** environ; int main(void) { printf("test pid=%d\n", getpid()); int i; for (i=0; environ[i]!=NULL; ++i) { printf("%s\n", environ[i]); } return 0; }

运行结果:

[^_^ ~/gdb 17:14 113]$./execl Entering main ... test pid=3846 HOSTNAME=devCompile-Server.sh.gj.com M2=/usr/local/apache-maven-3.5.0/bin TERM=xterm SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=172.30.28.162 53075 22 SSH_TTY=/dev/pts/3 LC_ALL=en_US.UTF-8

当然环境变量有很多,这里只截取了部分粘贴过来了。(在终端可以用env查看环境变量的值)

execle eg1. C: execle.c

#include #include #include int main(int argc, char *argv[]) { char * const env[] = {"AA=11", "BB=22", NULL}; printf("Entering main ...\n"); int ret; ret =execle("./test", "test", NULL, env); if(ret == -1) perror("execl error"); printf("Exiting main ...\n"); return 0; }

test.c

#include #include extern char** environ; int main(void) { printf("test pid=%d\n", getpid()); int i; for (i=0; environ[i]!=NULL; ++i) { printf("%s\n", environ[i]); } return 0; }

运行结果:

[^_^ ~/gdb 17:21 126]$./execle Entering main ... test pid=18007 AA=11 BB=22

在execle.c中定义的env的值,会传给execle

进程调度

调度优先级和调度策略由内核决定。 进程可以通过调整nice值,来调整优先级。 nice值越小,优先级越高。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有